package waskung

import (
	"database/sql"
	"html/template"
)

// LoadOptions adds <option> elements based on the result of an SQL query.
// The query should return two or three columns: value, label, and optionally
// selected.
func (h *HTMLWriter) LoadOptions(db *sql.DB, query string, args ...interface{}) {
	rows, err := db.Query(query, args...)
	if err != nil {
		h.Error(err)
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		h.Error(err)
		return
	}

	for rows.Next() {
		var value, label string
		var selected sql.NullBool
		if len(cols) == 2 {
			err = rows.Scan(&value, &label)
		} else {
			err = rows.Scan(&value, &label, &selected)
		}
		if err != nil {
			h.Error(err)
			return
		}
		t := h.Tag("option").Attr("value", value)
		if selected.Valid && selected.Bool {
			t.Attr("selected", "")
		}
		t.Text(label)
	}
	err = rows.Err()
	if err != nil {
		h.Error(err)
	}
}

// LoadLinkedRows adds <tr> elements based on the result of an SQL query. The
// first column of the results is used as a URL to go to when the row is
// clicked; the rest are used as the contents of <td> elements.
func (h *HTMLWriter) LoadLinkedRows(db *sql.DB, query string, args ...interface{}) {
	rows, err := db.Query(query, args...)
	if err != nil {
		h.Error(err)
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		h.Error(err)
		return
	}

	vals := make([]sql.NullString, len(cols))
	pointers := make([]interface{}, len(cols))
	for i := range vals {
		pointers[i] = &vals[i]
	}

	for rows.Next() {
		err = rows.Scan(pointers...)
		if err != nil {
			h.Error(err)
			return
		}
		h.Tr().OnClick("window.location.href = '" + template.JSEscapeString(vals[0].String) + "'").With(func() {
			for _, v := range vals[1:] {
				h.Td().Text(v.String)
			}
		})
	}
	err = rows.Err()
	if err != nil {
		h.Error(err)
	}
}

// LoadLinkedClassyRows adds <tr> elements based on the result of an SQL query.
// The first column of the results is used as a URL to go to when the row is
// clicked; the second is the class attribute for the <tr> element; the rest
// are used as the contents of <td> elements.
func (h *HTMLWriter) LoadLinkedClassyRows(db *sql.DB, query string, args ...interface{}) {
	rows, err := db.Query(query, args...)
	if err != nil {
		h.Error(err)
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		h.Error(err)
		return
	}

	vals := make([]sql.NullString, len(cols))
	pointers := make([]interface{}, len(cols))
	for i := range vals {
		pointers[i] = &vals[i]
	}

	for rows.Next() {
		err = rows.Scan(pointers...)
		if err != nil {
			h.Error(err)
			return
		}
		tr := h.Tr()
		tr.OnClick("window.location.href = '" + template.JSEscapeString(vals[0].String) + "'")
		if vals[1].Valid {
			tr.Class(vals[1].String)
		}
		tr.With(func() {
			for _, v := range vals[2:] {
				h.Td().Text(v.String)
			}
		})
	}
	err = rows.Err()
	if err != nil {
		h.Error(err)
	}
}

// LoadRows adds <tr> elements based on the result of an SQL query. The columns
// of the result are used as the contents of <td> elements.
func (h *HTMLWriter) LoadRows(db *sql.DB, query string, args ...interface{}) {
	rows, err := db.Query(query, args...)
	if err != nil {
		h.Error(err)
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		h.Error(err)
		return
	}

	vals := make([]sql.NullString, len(cols))
	pointers := make([]interface{}, len(cols))
	for i := range vals {
		pointers[i] = &vals[i]
	}

	for rows.Next() {
		err = rows.Scan(pointers...)
		if err != nil {
			h.Error(err)
			return
		}
		h.Tr().With(func() {
			for _, v := range vals {
				h.Td().Text(v.String)
			}
		})
	}
	err = rows.Err()
	if err != nil {
		h.Error(err)
	}
}

// LoadText executes an SQL query returning a single value, and adds that value
// as a text node.
func (h *HTMLWriter) LoadText(db *sql.DB, query string, args ...interface{}) {
	row := db.QueryRow(query, args...)
	var s sql.NullString
	err := row.Scan(&s)
	if err != nil {
		h.Error(err)
		return
	}
	if s.Valid {
		h.Text(s.String)
	}
}
